#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <sstream>
#include <algorithm>
#include <vector>
#include <climits>
using namespace std;

struct Access {
    int addr; 
    int size; 
    long long start; 
    long long time; 
};//用于保存每次内存访问的四元组信息：addr、size、start和time

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int L, M, N;
    if (!(cin >> L >> M >> N)) return 0;
    vector<Access> a(N);
    for (int i = 0; i < N; ++i) cin >> a[i].addr >> a[i].size >> a[i].start >> a[i].time;
    
    // 根据start时间将访问请求分组
    vector<vector<int>> groups;
    for (int i = 0; i < N; ) {
        int j = i+1;
        while (j < N && a[j].start == a[i].start) ++j;
        groups.emplace_back();
        for (int k = i; k < j; ++k) groups.back().push_back(k);
        i = j;
    }
    
    // loaded表示当前内存中已加载的区间
    vector<pair<int,int>> loaded;
    // merge_insert函数：将区间[Lx, Rx)插入loaded，合并重叠区间
    auto merge_insert = [&](int Lx, int Rx){
        if (Lx >= Rx) return;
        vector<pair<int,int>> res;
        int l = Lx, r = Rx;
        bool placed = false;
        for (auto &it : loaded) {
            if (it.second < l) res.push_back(it);
            else if (it.first > r) {
                if (!placed) { res.push_back({l,r}); placed = true; }
                res.push_back(it);
            } else {
                l = min(l, it.first);
                r = max(r, it.second);
            }
        }
        if (!placed) res.push_back({l,r});
        loaded.swap(res);
    };
    // remove_interval函数：将区间[Lx, Rx)从loaded中移除
    auto remove_interval = [&](int Lx, int Rx){
        if (Lx >= Rx) return;
        vector<pair<int,int>> res;
        for (auto &it : loaded) {
            if (it.second <= Lx || it.first >= Rx) res.push_back(it);
            else {
                if (it.first < Lx) res.push_back({it.first, Lx});
                if (it.second > Rx) res.push_back({Rx, it.second});
            }
        }
        loaded.swap(res);
    };
    // total_loaded函数：计算当前内存中已加载区间的总长度
    auto total_loaded = [&](){
        long long s = 0;
        for (auto &it : loaded) s += (it.second - it.first);
        return s;
    };
    // merge_intervals函数：合并重叠区间
    auto merge_intervals = [&](vector<pair<int,int>> v){
        if (v.empty()) return v;
        sort(v.begin(), v.end());
        vector<pair<int,int>> res;
        res.push_back(v[0]);
        for (size_t i = 1; i < v.size(); ++i) {
            if (v[i].first < res.back().second) res.back().second = max(res.back().second, v[i].second);
            else res.push_back(v[i]);
        }
        return res;
    };
    // build_req函数：构建一组访问请求的区间列表并合并重叠区间
    auto build_req = [&](const vector<int>& g)->vector<pair<int,int>> {
        vector<pair<int,int>> req;
        for (int idx : g) req.emplace_back(a[idx].addr, a[idx].addr + a[idx].size);
        return merge_intervals(req);
    };
    // needed_parts函数：计算在req中需要加载但当前loaded中未加载的区间
    auto needed_parts = [&](const vector<pair<int,int>>& req)->vector<pair<int,int>> {
        vector<pair<int,int>> need;
        for (auto &r : req) {
            int cur = r.first;
            for (auto &it : loaded) {
                if (it.second <= cur) continue;
                if (it.first >= r.second) break;
                if (it.first > cur) { need.emplace_back(cur, min(r.second, it.first)); cur = min(r.second, it.second); }
                else cur = max(cur, it.second);
                if (cur >= r.second) break;
            }
            if (cur < r.second) need.emplace_back(cur, r.second);
        }
        return merge_intervals(need);
    };

    // 预先计算基本区间和每个基本区间对应的组列表（用于后续的下次使用时间计算）
    vector<vector<pair<int,int>>> group_reqs(groups.size());
    for (size_t gi = 0; gi < groups.size(); ++gi) group_reqs[gi] = build_req(groups[gi]);
    vector<long long> group_end(groups.size(), 0); // 记录每组计算任务的结束时间
    // 构建基本区间列表b和每个基本区间对应的组列表seg_groups
    vector<int> b;
    b.reserve(2 * N + 4);
    for (size_t gi = 0; gi < group_reqs.size(); ++gi) {
        for (auto &seg : group_reqs[gi]) { b.push_back(seg.first); b.push_back(seg.second); }
    }
    sort(b.begin(), b.end());
    b.erase(unique(b.begin(), b.end()), b.end());
    int S = max(0, (int)b.size() - 1);
    vector<pair<int,int>> prim(S);
    for (int i = 0; i < S; ++i) prim[i] = { b[i], b[i+1] };
    vector<vector<int>> seg_groups(S);
    for (size_t gi = 0; gi < group_reqs.size(); ++gi) {
        for (auto &seg : group_reqs[gi]) {
            int li = int(lower_bound(b.begin(), b.end(), seg.first) - b.begin());
            int ri = int(lower_bound(b.begin(), b.end(), seg.second) - b.begin());
            for (int sid = li; sid < ri; ++sid) seg_groups[sid].push_back((int)gi);
        }
    }
    for (int sid = 0; sid < S; ++sid) {
        auto &v = seg_groups[sid];
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());
    }
    // interval_next_use函数：计算区间[Lx, Rx)的下次使用时间（对应的组索引）    
    auto interval_next_use = [&](int Lx, int Rx, int cur_gi)->int {
        if (Lx >= Rx) return INT_MAX;
        if (S == 0) return INT_MAX;
        int li = int(lower_bound(b.begin(), b.end(), Lx) - b.begin());
        int ri = int(lower_bound(b.begin(), b.end(), Rx) - b.begin());
        int earliest = INT_MAX; // 下次使用的最早组索引
        for (int sid = li; sid < ri; ++sid) {
            auto &gv = seg_groups[sid];
            if (gv.empty()) continue;
            auto it = upper_bound(gv.begin(), gv.end(), cur_gi);
            if (it != gv.end()) earliest = min(earliest, *it);
            else {
                //如果该基本区间在当前组之后没有被使用，返回INT_MAX表示永远不会再被使用
                return INT_MAX;
            }
        }
        return earliest == INT_MAX ? INT_MAX : earliest;
    };

    long long rw_time = 0;   // read/write操作的时间线
    long long comp_time = 0; // 计算任务的时间线
    vector<string> out;

    for (size_t gi = 0; gi < groups.size(); ++gi) {
        auto &g = groups[gi];
        auto req = group_reqs[gi];
        // 计算需要加载的区间
        auto need = needed_parts(req);
        long long need_bytes = 0;
        for (auto &p : need) need_bytes += (p.second - p.first);
        long long cur_loaded = total_loaded();

        // 定义一个函数来生成当前时间点需要保护的区间列表
        auto make_forbidden = [&](long long time_point)->vector<pair<int,int>> {
            // 当前组的请求区间加上所有在time_point时仍在运行的组的请求区间
            vector<pair<int,int>> forb = req;
            for (size_t k = 0; k < groups.size(); ++k) {
                if (k == gi) continue;
                if (group_end[k] > time_point) {
                    forb.insert(forb.end(), group_reqs[k].begin(), group_reqs[k].end());
                }
            }
            return merge_intervals(forb);
        };

        if (need_bytes + cur_loaded > M) {
            // 这里内存不够加载所有需要的片段，尝试卸载不与当前保护区间重叠的区间以释放空间
            auto forbidden = make_forbidden(rw_time);
            vector<pair<int,int>> freeable;
            for (auto &seg : loaded) {
                int Lx = seg.first, Rx = seg.second, cur = Lx;
                for (auto &f : forbidden) {
                    if (f.second <= cur) continue;
                    if (f.first >= Rx) break;
                    if (f.first > cur) { freeable.emplace_back(cur, min(f.first, Rx)); cur = min(Rx, f.second); }
                    else cur = max(cur, f.second);
                    if (cur >= Rx) break;
                }
                if (cur < Rx) freeable.emplace_back(cur, Rx);
            }
            freeable = merge_intervals(freeable);
            // 寻找可以卸载的区间，优先卸载那些未来不会再被访问的区间，其次卸载那些未来被访问时间最远的区间
            struct Cand { pair<int,int> seg; int next_use; };
            vector<Cand> cands; cands.reserve(freeable.size());
            for (auto &seg : freeable) {
                int nu = interval_next_use(seg.first, seg.second, (int)gi);
                cands.push_back({seg, nu});
            }
            sort(cands.begin(), cands.end(), [](const Cand &A, const Cand &B){
                // 未来不会再被访问的区间（INT_MAX）优先卸载，其次未来被访问时间最远的区间优先卸载
                bool An = (A.next_use == INT_MAX), Bn = (B.next_use == INT_MAX);
                if (An != Bn) return An; // An true -> come first
                if (A.next_use != B.next_use) return A.next_use > B.next_use;
                return A.seg.first < B.seg.first;
            });
            long long need_free = max(0LL, need_bytes + cur_loaded - (long long)M);
            for (auto &it : cands) {
                if (need_free <= 0) break;
                int Lx = it.seg.first, Rx = it.seg.second;
                int len = Rx - Lx; if (len <= 0) continue;
                int take = (int)min<long long>(len, need_free);
                int offL = Lx, offR = Lx + take;
                out.push_back("Offload " + to_string(rw_time) + " " + to_string(offL) + " " + to_string(take));
                rw_time += 40LL * take;
                remove_interval(offL, offR);
                cur_loaded -= take;
                need_free -= take;
            }
        }

        // 如果仍然内存不够，则等待计算任务完成后再卸载
        if (need_bytes + cur_loaded > M) {
            if (rw_time < comp_time) rw_time = comp_time; //计算任务完成后才能卸载
            auto forbidden2 = make_forbidden(rw_time);
            vector<pair<int,int>> freeable2;
            for (auto &seg : loaded) {
                int Lx = seg.first, Rx = seg.second, cur = Lx;
                for (auto &f : forbidden2) {
                    if (f.second <= cur) continue;
                    if (f.first >= Rx) break;
                    if (f.first > cur) { freeable2.emplace_back(cur, min(f.first, Rx)); cur = min(Rx, f.second); }
                    else cur = max(cur, f.second);
                    if (cur >= Rx) break;
                }
                if (cur < Rx) freeable2.emplace_back(cur, Rx);
            }
            freeable2 = merge_intervals(freeable2);
            struct Cand2 { pair<int,int> seg; int next_use; };
            vector<Cand2> cands2; cands2.reserve(freeable2.size());
            for (auto &seg : freeable2) {
                int nu = interval_next_use(seg.first, seg.second, (int)gi);
                cands2.push_back({seg, nu});
            }
            sort(cands2.begin(), cands2.end(), [](const Cand2 &A, const Cand2 &B){
                bool An = (A.next_use == INT_MAX), Bn = (B.next_use == INT_MAX);
                if (An != Bn) return An;
                if (A.next_use != B.next_use) return A.next_use > B.next_use;
                return A.seg.first < B.seg.first;
            });
            long long need_free2 = max(0LL, need_bytes + cur_loaded - (long long)M);
            for (auto &it : cands2) {
                if (need_free2 <= 0) break;
                int Lx = it.seg.first, Rx = it.seg.second;
                int len = Rx - Lx; if (len <= 0) continue;
                int take = (int)min<long long>(len, need_free2);
                int offL = Lx, offR = Lx + take;
                out.push_back("Offload " + to_string(rw_time) + " " + to_string(offL) + " " + to_string(take));
                rw_time += 40LL * take;
                remove_interval(offL, offR);
                cur_loaded -= take;
                need_free2 -= take;
            }
        }

        // 作为最后的手段，卸载剩余加载的最小的整个区间（但仍避免当前保护的区间）
        if (need_bytes + cur_loaded > M) {
            vector<pair<int,int>> snap = loaded; // whole segments
            sort(snap.begin(), snap.end(), [](const pair<int,int>& x,const pair<int,int>& y){
                return (x.second-x.first) < (y.second-y.first);
            });
            auto forbidden_now = make_forbidden(rw_time);
            for (auto seg : snap) {
                long long need_free3 = max(0LL, need_bytes + cur_loaded - (long long)M);
                if (need_free3 <= 0) break;
                // 跳过与当前保护区间重叠的区间
                bool ok = true;
                for (auto &f : forbidden_now) if (!(seg.second <= f.first || seg.first >= f.second)) { ok = false; break; }
                if (!ok) continue;
                // 计算该整个区间的下次使用时间
                int nu = interval_next_use(seg.first, seg.second, (int)gi);
                // 推迟未来还会被访问的区间，优先卸载未来不会被访问的区间
                if (nu != INT_MAX) {
                    snap.push_back(seg);
                    continue;
                }
                int Lx = seg.first, Rx = seg.second; int len = Rx - Lx;
                if (len <= 0) continue;
                int take = (int)min<long long>(len, need_free3);
                int offL = Lx, offR = Lx + take;
                out.push_back("Offload " + to_string(rw_time) + " " + to_string(offL) + " " + to_string(take));
                rw_time += 40LL * take;
                remove_interval(offL, offR);
                cur_loaded -= take;
            }
        }

        // 加载当前组所需的区间
        need = needed_parts(req); // 重新计算需要加载的区间
        // 记录加载当前组所需区间前的rw_time
        long long rw_before_group_load = rw_time;
        for (auto &p : need) {
            int Lx = p.first, Rx = p.second; int len = Rx - Lx;
            if (len <= 0) continue;
            out.push_back("Reload " + to_string(rw_time) + " " + to_string(Lx) + " " + to_string(len));
            rw_time += 40LL * len;
            merge_insert(Lx, Rx);
       }
        // 保存加载完所需区间后的rw_time
        long long group_rw_end = rw_time;
        // ---------- 预取下一组的访问 ----------
        // 尝试预取下一组中按输入顺序的整个访问。
        // 只有当该访问的所有缺失部分都能放入当前空闲的HBM时，才进行预取。
        if (gi + 1 < groups.size()) {
            long long free_bytes = M - total_loaded();
            for (int idx : groups[gi+1]) {
                vector<pair<int,int>> single_req = { {a[idx].addr, a[idx].addr + a[idx].size} };
                auto need_next = needed_parts(single_req);
                long long need_next_bytes = 0;
                for (auto &q : need_next) need_next_bytes += (q.second - q.first);
                if (need_next_bytes == 0) continue; //已经全部加载
                if (need_next_bytes <= free_bytes) {
                    // 预取该访问所有缺失部分
                    for (auto &q : need_next) {
                        int Lp = q.first, Rp = q.second, len = Rp - Lp;
                        if (len <= 0) continue;
                        out.push_back("Reload " + to_string(rw_time) + " " + to_string(Lp) + " " + to_string(len));
                        rw_time += 40LL * len;
                        merge_insert(Lp, Rp);
                        free_bytes -= len;
                    }
                } else {
                }
                if (free_bytes <= 0) break;
            }
        }
        // 访问必须在组间串行化：下一组的访问不能早于comp_time开始
        long long visit_start = max(comp_time, (long long)a[g.front()].start);
        // 也不能早于重载完成时间
        visit_start = max(visit_start, group_rw_end);
        long long group_dur = 0;
        for (int idx : g) group_dur = max(group_dur, a[idx].time);
        for (int idx : g) out.push_back("Visit " + to_string(visit_start) + " " + to_string(idx));
        comp_time = visit_start + group_dur;
        group_end[gi] = comp_time;
    }

    long long finish_time = max(rw_time, comp_time);
    out.push_back("Fin " + to_string(finish_time));
    // 按时间升序输出；同时间时按类型优先级：Visit < Reload < Offload < Fin ，
    // 同类型同时间保持生成顺序。
    struct Op { long long t; int pr; int idx; string line; };
    vector<Op> ops; ops.reserve(out.size());
    for (int i = 0; i < (int)out.size(); ++i) {
        const string &line = out[i];
        stringstream ss(line);
        string op; long long t = LLONG_MAX;
        if (ss >> op) {
            ss >> t; 
        }
        int pr = 4;
        if (op == "Visit") pr = 0;
        else if (op == "Reload") pr = 1;
        else if (op == "Offload") pr = 2;
        else if (op == "Fin") pr = 3;
        ops.push_back({t, pr, i, line});
    }
    sort(ops.begin(), ops.end(), [](const Op &a, const Op &b){
        if (a.t != b.t) return a.t < b.t;
        if (a.pr != b.pr) return a.pr < b.pr;
        return a.idx < b.idx;
    });
    for (auto &op : ops) cout << op.line << '\n';
    return 0;
}